package com.lu.config;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.lu.filter.JwtAuthenticationFilter;
import com.lu.filter.JwtLoginFilter;
import com.lu.model.entity.SysUser;
import com.lu.model.response.Result;
import com.lu.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.util.DigestUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @program lu-boot-saas
 * @description: 权限架构 - 配置类
 * @author: zhanglu
 * @create: 2022-09-26 14:25:00
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = false, securedEnabled =true) //激活方法上的PreAuthorize注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private SysUserService userService;
  @Autowired
  private RedisTemplate<String, Object> redisTemplate;
  @Autowired
  private RedisTemplate<String, Object> redisTemplateStrategy;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.headers().frameOptions().disable();
    http.cors()
        .and()
        .csrf().disable()
        .authorizeRequests()
        .antMatchers("/login").permitAll()
        .anyRequest().authenticated()
        .and()
        .addFilter(new JwtLoginFilter(authenticationManager(), userService, redisTemplate, redisTemplateStrategy))
        .addFilter(new JwtAuthenticationFilter(authenticationManager(), userService, redisTemplate));
    //配置自定义认证、授权异常处理器
    http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
      @Override
      public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setContentType("application/json; charset=utf-8");
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.getWriter().write(JSON.toJSONString(Result.error(-1, "认证失败，请重新登录!")));
      }
    }).accessDeniedHandler(new AccessDeniedHandler() {
      @Override
      public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(HttpStatus.FORBIDDEN.value());
        response.getWriter().write(JSON.toJSONString(Result.error(-1, "当前权限不足!")));
      }
    });
  }

  @Override
  public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userService).passwordEncoder(new PasswordEncoder() {
      //对密码进行加密
      @Override
      public String encode(CharSequence charSequence) {
        return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
      }
      //对密码进行判断匹配
      @Override
      public boolean matches(CharSequence charSequence, String pwd) {
        String encode = DigestUtils.md5DigestAsHex(StrUtil.bytes((charSequence.toString() + SysUser.SALT), CharsetUtil.UTF_8));
        boolean res = pwd.equals(encode);
        return res;
      }
    } );
  }

}
